package org.redmagic.agent.spring;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.redmagic.PlaceholderExtractor;
import org.redmagic.PlaceholderReplacer;
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.StringUtils;

public class DefaultPropertyPlaceholderHelper extends PropertyPlaceholderHelper implements PlaceholderExtractor, PlaceholderReplacer {

    private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<String, String>(4);

    static {
        wellKnownSimplePrefixes.put("}", "{");
        wellKnownSimplePrefixes.put("]", "[");
        wellKnownSimplePrefixes.put(")", "(");
    }
    
    private final String placeholderPrefix;
    private final String placeholderSuffix;
    private final String simplePrefix;
    
    public DefaultPropertyPlaceholderHelper() {
        this(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX,
             PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_SUFFIX);
    }
    
    public DefaultPropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
        super(placeholderPrefix, placeholderSuffix);

        this.placeholderPrefix = placeholderPrefix;
        this.placeholderSuffix = placeholderSuffix;

        String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
        if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
            this.simplePrefix = simplePrefixForSuffix;
        }
        else {
            this.simplePrefix = this.placeholderPrefix;
        }
    }

    @Override
    public Set<String> extractPlaceholders(String strValue) {
        Set<String> placeholders = new HashSet<String>();
        resolvePlaceholders(strValue, placeholders);
        return placeholders;
    }

    private void resolvePlaceholders(String value, Set<String> placeholders) {
        StringBuilder buf = new StringBuilder(value);

        int startIndex = value.indexOf(this.placeholderPrefix);
        while (startIndex != -1) {
            int endIndex = findPlaceholderEndIndex(buf, startIndex);
            if (endIndex != -1) {
                String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
                if (!placeholders.add(placeholder)) {
                    return;
                }
                
                // Recursive invocation, parsing placeholders contained in the placeholder key.
                resolvePlaceholders(placeholder, placeholders);
            }
            else {
                startIndex = -1;
            }
        }
    }
    
    private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
        int index = startIndex + this.placeholderPrefix.length();
        int withinNestedPlaceholder = 0;
        while (index < buf.length()) {
            if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
                if (withinNestedPlaceholder > 0) {
                    withinNestedPlaceholder--;
                    index = index + this.placeholderSuffix.length();
                }
                else {
                    return index;
                }
            }
            else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
                withinNestedPlaceholder++;
                index = index + this.simplePrefix.length();
            }
            else {
                index++;
            }
        }
        return -1;
    }
    
    public static void main(String[] args) {
    	DefaultPropertyPlaceholderHelper dph = new DefaultPropertyPlaceholderHelper();
    	Set<String> placeholders = dph.extractPlaceholders("${p1${p2}}${p3}");
    	
    	for (String placeholder : placeholders) {
    		System.out.println(placeholder);
    	}
    }
}